﻿using System;
using System.IO;
using System.Threading;
using Microsoft.SqlServer.Dts.Runtime;

namespace SSISFileWatcherTask100.FileWatch
{
    public delegate void StartEventHandler(object sender, EventArgs e);

    public class FileWatcherCore : IDisposable
    {
        private readonly FileSystemWatcher _fileSystemWatcher;
        public event StartEventHandler Started;
        //private readonly ManualResetEvent manualResetEvent = new ManualResetEvent(false);
        private volatile bool _exit;
        private volatile bool _started;
        private Timer _timer;
        private IDTSComponentEvents _componentEvents;

        private string FileFilter
        {
            get;
            set;
        }

        public string ListeningPath
        {
            get
            {
                return _fileSystemWatcher.Path;
            }
            set
            {
                _fileSystemWatcher.Path = value;
            }
        }

        public string FullFileName { get; set; }

        private string FireOn { get; set; }

        private int TimeOut { get; set; }
        private bool refire;

        public FileWatcherCore()
        {
            _started = false;
        }

        public FileWatcherCore(IDTSComponentEvents componentEvents, string listeningPath, string fileFilter, bool includeSubdirectories, string fireOn, int timeOut)
        {
            _started = false;
            _componentEvents = componentEvents;
            _fileSystemWatcher = new FileSystemWatcher
                                     {
                                         Filter = fileFilter,
                                         IncludeSubdirectories = includeSubdirectories,
                                         Path = listeningPath
                                     };

            FireOn = fireOn;
            TimeOut = timeOut;

            if (TimeOut > 0)
            {
                _componentEvents.FireInformation(0, "SSISFileWatcher", "Initialize Timeout", string.Empty, 0, ref refire);
                _timer = new Timer(StopWatching);
                _timer.Change(timeOut * 1000, 0);
            }

            _started = false;
        }

        public void StartWatching()
        {
            if (!_started)
            {
                SubscribeToFileSystemWatcherEvents();
                Started += FileWatcherCore_Started;
                _started = true;
            }
        }

        public void StopWatching(object state)
        {
            Timer timer = (Timer)state;
            timer.Dispose();
            StopSubscribedEvents();
        }

        public void StopWatching()
        {
            StopSubscribedEvents();
        }

        private void StopSubscribedEvents()
        {
            _componentEvents.FireInformation(0, "SSISFileWatcher", "Unsubscribe from events", string.Empty, 0, ref refire);
            var events = FireOn.Split(',');
            Array.ForEach(events, @event =>
                                      {
                                          if (@event == "OnChanged")
                                              _fileSystemWatcher.Changed += OnFile;
                                          if (@event == "OnCreate")
                                              _fileSystemWatcher.Created += OnFile;
                                          if (@event == "OnDelete")
                                              _fileSystemWatcher.Deleted += OnDeleted;
                                          if (@event == "OnRename")
                                              _fileSystemWatcher.Renamed += OnRenamed;
                                      });
        }

        void FileWatcherCore_Started(object sender, EventArgs e)
        {

            while (!_exit)
            {
                if (_exit)
                {
                    break;
                }
            }
        }

        private void SubscribeToFileSystemWatcherEvents()
        {
            _fileSystemWatcher.Filter = FileFilter;
            _fileSystemWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite;

            _componentEvents.FireInformation(0, "SSISFileWatcher", "Subscribe to events", string.Empty, 0, ref refire);

            var events = FireOn.Split(',');
            Array.ForEach(events, @event =>
                                    {
                                        if (@event == "OnChanged")
                                            _fileSystemWatcher.Changed += OnFile;
                                        if (@event == "OnCreate")
                                            _fileSystemWatcher.Created += OnFile;
                                        if (@event == "OnDelete")
                                            _fileSystemWatcher.Deleted += OnDeleted;
                                        if (@event == "OnRename")
                                            _fileSystemWatcher.Renamed += OnRenamed;
                                    });

            _fileSystemWatcher.Error += OnBufferError;
            _fileSystemWatcher.EnableRaisingEvents = true;

            _exit = false;

            FileWatcherCore_Started(this, new EventArgs());
        }

        void OnFile(object sender, FileSystemEventArgs e)
        {
            _fileSystemWatcher.EnableRaisingEvents = false;

            _timer.Dispose();
            //Thread t = new Thread(new ParameterizedThreadStart(CopyFile));
            //t.Start(fileChange.FullPath);

            _componentEvents.FireInformation(0, "SSISFileWatcher", string.Format("The file {0} has been catched for {1} event", e.FullPath, e.ChangeType.ToString()), string.Empty, 0, ref refire);
            _componentEvents.FireInformation(0, "SSISFileWatcher", "Wait...", string.Empty, 0, ref refire);

            FileWatcherTools.WaitReady(e.FullPath);
            FullFileName = e.FullPath;

            if (_fileSystemWatcher != null)
            {
                _fileSystemWatcher.Dispose();
            }
            _componentEvents.FireInformation(0, "SSISFileWatcher", "Dispose the File Watcher", string.Empty, 0, ref refire);
            _exit = true;
        }

        private void OnBufferError(object sender, ErrorEventArgs e)
        {
            _componentEvents.FireError(0, "SSISFileWatcher", "Buffer error. Please consider the message from the catched exception", string.Empty, 0);
            throw new Exception(e.GetException().Message);
        }

        private void OnRenamed(object sender, RenamedEventArgs e)
        {
            _componentEvents.FireInformation(0, "SSISFileWatcher", string.Format("OnRenamed {0}", e.FullPath), string.Empty, 0, ref refire);
            FileWatcherTools.WaitReady(e.FullPath);
        }

        private void OnDeleted(object sender, FileSystemEventArgs e)
        {
            _componentEvents.FireInformation(0, "SSISFileWatcher", string.Format("OnDeleted {0}", e.FullPath), string.Empty, 0, ref refire);
            FileWatcherTools.WaitReady(e.FullPath);
        }

        private void OnCreated(object sender, FileSystemEventArgs e)
        {
            _componentEvents.FireInformation(0, "SSISFileWatcher", string.Format("OnDeleted {0}", e.FullPath), string.Empty, 0, ref refire);
            FileWatcherTools.WaitReady(e.FullPath);

            //Thread thread = new Thread(ThreadLoop);
            //thread.Start((Action)CallBack);
        }

        private void OnChanged(object sender, FileSystemEventArgs e)
        {
            FileWatcherTools.WaitReady(e.FullPath);
        }

        //private static void ThreadLoop(object callBack)
        //{
        //    while (true)
        //    {
        //        ((Delegate)callBack).DynamicInvoke(callBack);
        //        Thread.Sleep(500);
        //    }
        //}

        //private static void CallBack()
        //{
        //    FileWatcherTools statusChecker = new FileWatcherTools();
        //    statusChecker.WaitReady();
        //}

        public void Dispose()
        {
            if (_timer != null)
                _timer.Dispose();

            StopSubscribedEvents();

            _fileSystemWatcher.Dispose();
        }
    }

    internal class FileWatcherTools
    {
        public static void WaitReady(string fileName)
        {
            while (true)
            {
                try
                {
                    using (Stream stream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
                    {
                        if (stream != null)
                        {
                            System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName));
                            break;
                        }
                    }
                }
                catch (FileNotFoundException ex)
                {
                    System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
                }
                catch (IOException ex)
                {
                    System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
                }
                catch (UnauthorizedAccessException ex)
                {
                    System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
                }

                Thread.Sleep(500);
            }
        }
    }
}
